﻿namespace Fiddler
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading;

    internal class DefaultCertificateProvider : ICertificateProvider3, ICertificateProvider2, ICertificateProvider
    {
        private ReaderWriterLock _oRWLock = new ReaderWriterLock();
        private string _sMakeCertLocation = CONFIG.GetPath("MakeCert");
        private X509Certificate2 certRoot;
        private Dictionary<string, X509Certificate2> certServerCache = new Dictionary<string, X509Certificate2>();

        internal DefaultCertificateProvider()
        {
        }

        public bool CacheCertificateForHost(string sHost, X509Certificate2 oCert)
        {
            try
            {
                this.GetWriterLock();
                this.certServerCache[sHost] = oCert;
            }
            finally
            {
                this.FreeWriterLock();
            }
            return true;
        }

        public bool ClearCertificateCache()
        {
            return this.ClearCertificateCache(true);
        }

        public bool ClearCertificateCache(bool bRemoveRoot)
        {
            bool flag = true;
            try
            {
                X509Certificate2Collection certificates;
                this.GetWriterLock();
                this.certServerCache.Clear();
                this.certRoot = null;
                string sFullSubject = string.Format("CN={0}{1}", CONFIG.sMakeCertRootCN, CONFIG.sMakeCertSubjectO);
                if (bRemoveRoot)
                {
                    certificates = FindCertsBySubject(StoreName.Root, StoreLocation.CurrentUser, sFullSubject);
                    if (certificates.Count > 0)
                    {
                        X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
                        store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
                        try
                        {
                            store.RemoveRange(certificates);
                        }
                        catch
                        {
                            flag = false;
                        }
                        store.Close();
                    }
                }
                certificates = FindCertsByIssuer(StoreName.My, sFullSubject);
                if (certificates.Count <= 0)
                {
                    return flag;
                }
                if (!bRemoveRoot)
                {
                    X509Certificate2 rootCertificate = this.GetRootCertificate();
                    if (rootCertificate != null)
                    {
                        certificates.Remove(rootCertificate);
                        if (certificates.Count < 1)
                        {
                            return true;
                        }
                    }
                }
                X509Store store2 = new X509Store(StoreName.My, StoreLocation.CurrentUser);
                store2.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
                try
                {
                    store2.RemoveRange(certificates);
                }
                catch
                {
                    flag = false;
                }
                store2.Close();
            }
            finally
            {
                this.FreeWriterLock();
            }
            return flag;
        }

        private X509Certificate2 CreateCert(string sHostname, bool isRoot)
        {
            int num;
            string str;
            string str2;
            if (sHostname.IndexOfAny(new char[] { '"', '\r', '\n', '\0' }) != -1)
            {
                return null;
            }
            if (!isRoot && (this.GetRootCertificate() == null))
            {
                try
                {
                    this.GetWriterLock();
                    if ((this.GetRootCertificate() == null) && !this.CreateRootCertificate())
                    {
                        FiddlerApplication.DoNotifyUser("Creation of the root certificate was not successful.", "Certificate Error");
                        return null;
                    }
                }
                finally
                {
                    this.FreeWriterLock();
                }
            }
            if (!File.Exists(this._sMakeCertLocation))
            {
                FiddlerApplication.DoNotifyUser("Cannot locate:\n\t\"" + this._sMakeCertLocation + "\"\n\nPlease move makecert.exe to the Fiddler installation directory.", "MakeCert.exe not found");
                throw new FileNotFoundException("Cannot locate: " + this._sMakeCertLocation + ". Please move makecert.exe to the Fiddler installation directory.");
            }
            X509Certificate2 certificate = null;
            if (isRoot)
            {
                str2 = string.Format(CONFIG.sMakeCertParamsRoot, new object[] { sHostname, CONFIG.sMakeCertSubjectO, CONFIG.sMakeCertRootCN, DateTime.Now.AddDays(-7.0).ToString("MM/dd/yyyy", CultureInfo.InvariantCulture), FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.Root.extraparams", string.Empty) });
            }
            else
            {
                str2 = string.Format(CONFIG.sMakeCertParamsEE, new object[] { sHostname, CONFIG.sMakeCertSubjectO, CONFIG.sMakeCertRootCN, DateTime.Now.AddDays(-7.0).ToString("MM/dd/yyyy", CultureInfo.InvariantCulture), FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.EE.extraparams", string.Empty) });
            }
            try
            {
                X509Certificate2 certificate2;
                this.GetWriterLock();
                if (!this.certServerCache.TryGetValue(sHostname, out certificate2))
                {
                    certificate2 = LoadCertificateFromWindowsStore(sHostname);
                }
                if (certificate2 != null)
                {
                    if (CONFIG.bDebugCertificateGeneration)
                    {
                        FiddlerApplication.Log.LogFormat("/Fiddler.CertMaker>{1} A racing thread already successfully CreatedCert({0})", new object[] { sHostname, Thread.CurrentThread.ManagedThreadId });
                    }
                    return certificate2;
                }
                str = Utilities.GetExecutableOutput(this._sMakeCertLocation, str2, out num);
                if (CONFIG.bDebugCertificateGeneration)
                {
                    FiddlerApplication.Log.LogFormat("/Fiddler.CertMaker>{3}-CreateCert({0}) => ({1}){2}", new object[] { sHostname, num, (num == 0) ? "." : ("\r\n" + str), Thread.CurrentThread.ManagedThreadId });
                }
                if (num == 0)
                {
                    int num2 = 6;
                    do
                    {
                        certificate = LoadCertificateFromWindowsStore(sHostname);
                        Thread.Sleep((int) (50 * (6 - num2)));
                        if (CONFIG.bDebugCertificateGeneration && (certificate == null))
                        {
                            FiddlerApplication.Log.LogFormat("!WARNING: Couldn't find certificate for {0} on try #{1}", new object[] { sHostname, 6 - num2 });
                        }
                        num2--;
                    }
                    while ((certificate == null) && (num2 >= 0));
                }
                if (certificate != null)
                {
                    if (isRoot)
                    {
                        this.certRoot = certificate;
                    }
                    else
                    {
                        this.certServerCache[sHostname] = certificate;
                    }
                }
            }
            finally
            {
                this.FreeWriterLock();
            }
            if (certificate == null)
            {
                string sMessage = string.Format("Creation of the interception certificate failed.\n\nmakecert.exe returned {0}.\n\n{1}", num, str);
                FiddlerApplication.Log.LogFormat("Fiddler.CertMaker> [{0} {1}] Returned Error: {2} ", new object[] { this._sMakeCertLocation, str2, sMessage });
                if (CONFIG.bDebugCertificateGeneration)
                {
                    FiddlerApplication.DoNotifyUser(sMessage, "Unable to Generate Certificate");
                }
            }
            return certificate;
        }

        public bool CreateRootCertificate()
        {
            return (null != this.CreateCert(CONFIG.sMakeCertRootCN, true));
        }

        private static X509Certificate2Collection FindCertsByIssuer(StoreName storeName, string sFullIssuerSubject)
        {
            X509Store store = new X509Store(storeName, StoreLocation.CurrentUser);
            store.Open(OpenFlags.OpenExistingOnly);
            X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByIssuerDistinguishedName, sFullIssuerSubject, false);
            store.Close();
            return certificates;
        }

        private static X509Certificate2Collection FindCertsBySubject(StoreName storeName, StoreLocation storeLocation, string sFullSubject)
        {
            X509Store store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.OpenExistingOnly);
            X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, sFullSubject, false);
            store.Close();
            return certificates;
        }

        private void FreeReaderLock()
        {
            this._oRWLock.ReleaseReaderLock();
        }

        private void FreeWriterLock()
        {
            this._oRWLock.ReleaseWriterLock();
        }

        public X509Certificate2 GetCertificateForHost(string sHostname)
        {
            bool flag;
            try
            {
                this.GetReaderLock();
                if (this.certServerCache.ContainsKey(sHostname))
                {
                    return this.certServerCache[sHostname];
                }
            }
            finally
            {
                this.FreeReaderLock();
            }
            X509Certificate2 oCert = this.LoadOrCreateCertificate(sHostname, out flag);
            if ((oCert != null) && !flag)
            {
                this.CacheCertificateForHost(sHostname, oCert);
            }
            return oCert;
        }

        private void GetReaderLock()
        {
            this._oRWLock.AcquireReaderLock(-1);
        }

        public X509Certificate2 GetRootCertificate()
        {
            if (this.certRoot != null)
            {
                return this.certRoot;
            }
            X509Certificate2 certificate = LoadCertificateFromWindowsStore(CONFIG.sMakeCertRootCN);
            this.certRoot = certificate;
            return certificate;
        }

        private void GetWriterLock()
        {
            this._oRWLock.AcquireWriterLock(-1);
        }

        internal static X509Certificate2 LoadCertificateFromWindowsStore(string sHostname)
        {
            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            string b = string.Format("CN={0}{1}", sHostname, CONFIG.sMakeCertSubjectO);
            X509Certificate2Enumerator enumerator = store.Certificates.GetEnumerator();
            while (enumerator.MoveNext())
            {
                X509Certificate2 current = enumerator.Current;
                if (string.Equals(current.Subject, b, StringComparison.OrdinalIgnoreCase))
                {
                    store.Close();
                    return current;
                }
            }
            store.Close();
            return null;
        }

        internal X509Certificate2 LoadOrCreateCertificate(string sHostname, out bool bAttemptedCreation)
        {
            bAttemptedCreation = false;
            X509Certificate2 certificate = LoadCertificateFromWindowsStore(sHostname);
            if (certificate == null)
            {
                bAttemptedCreation = true;
                certificate = this.CreateCert(sHostname, false);
                if (certificate == null)
                {
                    FiddlerApplication.Log.LogFormat("!Fiddler.CertMaker> Tried to create cert for {0}, but can't find it from thread {1}!", new object[] { sHostname, Thread.CurrentThread.ManagedThreadId });
                }
            }
            return certificate;
        }

        public bool rootCertIsTrusted(out bool bUserTrusted, out bool bMachineTrusted)
        {
            bUserTrusted = 0 < FindCertsBySubject(StoreName.Root, StoreLocation.CurrentUser, string.Format("CN={0}{1}", CONFIG.sMakeCertRootCN, CONFIG.sMakeCertSubjectO)).Count;
            bMachineTrusted = 0 < FindCertsBySubject(StoreName.Root, StoreLocation.LocalMachine, string.Format("CN={0}{1}", CONFIG.sMakeCertRootCN, CONFIG.sMakeCertSubjectO)).Count;
            if (!bUserTrusted)
            {
                return bMachineTrusted;
            }
            return true;
        }

        public bool TrustRootCertificate()
        {
            X509Certificate2 rootCertificate = this.GetRootCertificate();
            if (rootCertificate == null)
            {
                return false;
            }
            try
            {
                X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
                store.Open(OpenFlags.ReadWrite);
                try
                {
                    store.Add(rootCertificate);
                }
                finally
                {
                    store.Close();
                }
                return true;
            }
            catch (Exception exception)
            {
                FiddlerApplication.Log.LogFormat("!Fiddler.CertMaker> Unable to auto-trust root: {0}", new object[] { exception });
                return false;
            }
        }
    }
}

